﻿#pragma once
#include "OpenGL项目主标头.h"  // 包含头文件，include 的相对路径就是当前文件所在路径
#include "OpenGL缓冲区类标头.h"
#include "OpenGL着色器类标头.h"
#include "OpenGL渲染器类标头.h"
#include "OpenGL纹理类标头.h"
#include "stblib/stb_image.h"
#include "OpenGL测试类标头.h"
#include "OpenGL颜色测试类标头.h"
#include "OpenGL纹理测试类标头.h"
// C/C++ 标准库学习网站：https://legacy.cplusplus.com
// C/C++ 微软(Visual Studio)中文学习网站：https://learn.microsoft.com/zh-cn/cpp/cpp/?view=msvc-170
// OpenGl 学习网站：https://www.khronos.org/opengl/wiki
// OpenGl 的 GLFW 学习网站：https://www.glfw.org/docs/latest/glfw3_8h.html#ac06b663d79c8fcf04669cc8fcc0b7670
// OpenGl 的 GLEW 学习网站：https://docs.gl/
// NODEFAULTLIB:library

/*
在使用 GLFW 库创建窗口之前，使用 OpenGL 构建窗口时可以添加依赖项 opengl32.lib、glfw3.lib、gdi32.lib、user32.lib、shell32.lib、advapi32.lib。此外可以添加动态库 glfw3dll.lib
    这里最主要的 opengl32.lib 是为了解决无法解析的外部符号 _imp_glClear 错误；而 user32.lib 是提供 windows 接口之类（包含了 Library 库）的需要；gdi32.lib 用于创建设备上下文和交换缓冲；shell32.lib 用于创建窗口，查询文件。

在 C++ 中可以使用 GLEW 库来管理 OpenGL 版本的兼容性。GLEW 是一个开源的跨平台的 C/C++ 库，它可以自动检测并加载 OpenGL 版本的兼容性。GLEW 库的下载地址为：https://github.com/nigels-com/glew。
    使用 GLEW 的 glewInit() 函数作为静态库需要定义 GLEW_STATIC 宏（在 glew.h 头文件中找到 glewInit() 函数的定义可以发现这点）：右键项目 -> 属性 -> C/C++ -> 预处理器 -> 预处理器定义，添加 GLEW_STATIC; 此外可以添加动态库 glew32.lib 需要忽略库取消4098警告，Debug 需要添加库 libcmt.lib 即可，Release 需要添加库 msvcrt.lib 和 libcmt.libs

OpenGL 具体的操作就是一个状态机，通过读取顶点缓存区数据进行绘制。具体的绘制操作需要通过调用 OpenGL 的 API 函数来实现。一切都是从顶点数据开始的。OpenGl 只有当坐标在 [-1,1] 的范围内才处理它，所以我们传入的顶点数据要在这个范围之内。Buffer 缓冲区会把一堆顶点数据，一起发给 GPU，在 OpenGL 中生成的所有东西都会被分配一个唯一的标识符，它只是一个整数，0 一般是一种无效状态，整数就是实际对象的 ID 可能是顶点缓冲区、顶点数组、纹理、着色器等，使用时可以就使用对应的数字（渲染三角形声明使用的缓冲区数字）

OpenGL 中的属性主要有：
    顶点属性：位置、颜色、纹理坐标、法向量等。调用次数相对较少
    顶点着色器：将顶点属性映射到片元属性的过程。调用次数相对较多
    片元着色器：将片元属性映射到屏幕上的颜色的过程。也叫像素这着色器
    几何着色器：将顶点数据转换为图元的过程。
    顶点缓存区：存放顶点数据的缓冲区。
    索引缓存区：存放索引数据的缓冲区。
    纹理：用于贴图的图像数据。
    帧缓存：用于显示渲染结果的缓冲区。
    着色器：用于处理顶点属性的程序。
    顶点数组对象：用于存储顶点属性的对象。
    纹理对象：用于存储纹理数据的对象。
    着色器程序对象：用于存储着色器程序的对象。
    帧缓冲对象：用于存储帧缓存的对象。
    几何着色器对象：用于处理顶点数据的对象。
    顶点缓冲对象：用于存储顶点数据的对象。
    索引缓冲对象：用于存储索引数据的对象。
    顶点数组对象：用于存储顶点属性的对象。
    纹理对象：用于存储纹理数据的对象。
    着色器程序对象：用于存储着色器程序的对象。
    帧缓冲对象：用于存储帧缓存的对象。
    几何着色器对象：用于处理顶点数据的对象。
    顶点缓冲对象：用于存储顶点数据的对象。
    索引缓冲对象：用于存储索引数据的对象。
    顶点数组对象：用于存储顶点属性的对象。

glGenBuffers 函数：
        创建一个缓冲对象，并将其 ID 存储在缓冲对象缓冲区中。缓冲对象可以是 VBO、EBO、FBO 等。这里的 VBO 表示顶点数据（vertex buffer object），EBO
    glBindBuffer 函数：
        将缓冲对象绑定到目标。这里的目标可以是 VBO、EBO、FBO 等。
    glBufferData() 函数：
        将数据写入缓冲对象。有两种办法：1.不需要马上提供数据，只需要提供偏移量和数据大小；2.提供完整的数据。
    glBufferSubData 函数：
        更新缓冲对象中的部分数据。
    glDeleteBuffers 函数：
        删除缓冲对象。
    glVertexAttribPointer 函数：
        定义顶点属性，第一个参数是属性的位置，第二个参数是属性的数量，第三个参数是属性的数据类型，第四个参数是是否需要归一化，第五个参数是偏移量（浮点数顶点字节数 + 纹理坐标字节数 + 法线字节数），第六个参数是数据指针。
    glEnableVertexAttribArray 函数：
        启用顶点属性，第一个参数是属性的位置。

一个注意点：如果创建文件选择头文件而命名修改后缀名为 .cpp 则需要修改该 cpp 文件的项类型为 C/C++ 编译器（右键该文件 -> 属性 -> 项类型 -> C/C++ 编译器）
*/
extern void dock_space();

static void Application(/*GLFWwindow* window*/) {
    GLFWwindow* window;
    /* Initialize the library 初始化库 */
    if (!glfwInit())
        //return -1;
        printf("初始化 GLFW 失败！\n");

    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); // 设置 OpenGL 版本为 4.4
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 4); // 设置 OpenGL 版本为 4.4
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 设置 OpenGL 版本为 Core Profile，如果设置 GLFW_OPENGL_COMPAT_PROFILE 表示兼容版本（默认的核心配置）， GLFW_OPENGL_CORE_PROFILE 需要指定一个不为零的顶点着色器对象

    /* Create a windowed mode window and its OpenGL context 创建窗口模式窗口及其 OpenGL 上下文 */
#pragma execution_character_set("utf-8")    // 设置编译环境为UTF-8编码，可用于网页显示中文
    window = glfwCreateWindow(1280, 960, "OpenGl基础", NULL, NULL);
#pragma execution_character_set("default")      // 恢复编译环境为默认编码
    //printf("窗口: %p\n", window); // 打印窗口指针
    if (!window) {
        glfwTerminate();
        //return -1;
        printf("窗口创建失败！\n");
        return;
    }

    //printf("new path is: %s\n", __FILE__);
    /* Make the window's context current 设置窗口的渲染上下文为当前 */
    glfwMakeContextCurrent(window);
    //glfwMakeContextCurrent(window2);  // window2 窗口，与 window 窗口共享 OpenGL 上下文

    glfwSwapInterval(1); // 设置垂直同步 (v-sync), 0 表示关闭垂直同步, 1 表示开启垂直同步

    /* Initialize GLEW 库 */
    if (glewInit() != GLEW_OK) // 加载 GLEW 库，需要创建一个有效的 OpenGL 渲染上下文
        //所以这个 glewInit() 函数必须在 glfwMakeContextCurrent() 函数之后调用
        cout << "GLEW initialization failed!" << endl;
    cout << "OpenGL version: " << glGetString(GL_VERSION) << endl; // 打印 OpenGL 版本
    {
        /*float position1[6] = { // 定义一个位置向量，这里定义了三角形的三个顶点坐标
            -0.5f, -0.5f,
             0.5f, -0.5f,
             0.0f,  0.5f
        }; */

        /*float position2[] = { // 定义另一个位置向量，四边形需要两个三角形，因为显卡倾向绘制三角形
            -0.5f, -0.5f,
             0.5f, -0.5f,
             0.5f,  0.5f,

             0.5f,  0.5f,
            -0.5f,  0.5f,
            -0.5f, -0.5f
        }; */

        gl_call(glEnable(GL_BLEND)); // 启用 GL 功能（启用混合）
        gl_call(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); // 设置混合函数，这里设置为源颜色和目标颜色的混合
        //gl_call(glBlendEquation(GL_FUNC_SUBTRACT)); // 设置混合等式，这里设置为加法混合

        float position3[] = { // 定义另一个位置向量，四边形需要两个三角形，因为显卡倾向绘制三角形
            100.0f, 75.0f,  0.0f, 0.0f,  // 前两个 float 值表示位置（顶点数组），后两个 float 值表示颜色（纹理）
            500.0f, 75.0f,  1.0f, 0.0f,
            500.0f, 400.0f, 1.0f, 1.0f,
            100.0f, 400.0f, 0.0f, 1.0f
        };  // 去掉重复的顶点

        unsigned int indices[] = { // 定义索引数组，这里定义了三角形的三个顶点索引
            0, 1, 2,    // 第一个三角形（使用 position3 的索引）
            2, 3, 0     // 第二个三角形
        };

        VertexArray vertex_array(1); // 定义顶点数组对象，这里传入 position3 顶点数据和 indices 索引数据
        /*unsigned int vertex_array_id;
        gl_call(glGenVertexArrays(1, &vertex_array_id)); // 生成顶点数组 VAO 对象
        cout << "VAO ID: " << vertex_array_id << endl; // 打印 VAO ID
        gl_call(glBindVertexArray(vertex_array_id)); // 绑定顶点数组对象 */

        VertexBuffer vertex_buffer(position3, 4 * 2 * 2 * sizeof(float)); // 包含纹理
        /*unsigned int vertex_buffer; // 可以去头文件中查看宏 glGenBuffers 的参数格式
        gl_call(glGenBuffers(1, &vertex_buffer)); // 生成 VBO 缓冲
        cout << "VBO ID: " << vertex_buffer << endl; // 打印 VBO ID（索引缓冲区的 ID）
        gl_call(glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer)); // 绑定 VBO 缓冲
        gl_call(glBufferData(GL_ARRAY_BUFFER, 4 * 2 * sizeof(float), position3, GL_STATIC_DRAW));  // 写入数据，不含纹理 */

        VertexBufferLayout layout; // 定义顶点缓冲布局，这里定义了两个顶点属性，位置和颜色
        layout.push<float>(2, false); // 定义位置属性，两个 float 值
        layout.push<float>(2, false); // 定义纹理属性，两个 float 值
        vertex_array.add_buffer(vertex_buffer, layout); // 向顶点数组对象中添加 VBO 缓冲
        /*gl_call(glEnableVertexAttribArray(0)); // 启用顶点属性，0 是索引，对应 glVertexAttribPointer 的第一个参数
        gl_call(glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, (const void*)0)); // 定义顶点属性 */

        IndexBuffer index_buffer(indices, 6);
        /*unsigned int index_buffer; // 可以去头文件中查看宏 glGenBuffers 的参数格式
        gl_call(glGenBuffers(1, &index_buffer)); // 生成 EBO 缓冲
        cout << "EBO ID: " << index_buffer << endl; // 打印 EBO ID（索引缓冲区的 ID）
        gl_call(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer)); // 绑定 EBO 缓冲
        gl_call(glBufferData(GL_ELEMENT_ARRAY_BUFFER, 6 * sizeof(unsigned int), indices, GL_STATIC_DRAW)); // 写入数据 */

        glm::mat4 glm_projection = glm::ortho(0.0f, 1200.0f, 0.0f, 900.0f, -1.0f, 1.0f); // 定义投影矩阵，前面 4 个参数是矩阵的左右下上（这里是 4 * 3 矩阵）最后两个参数表示远近
        //glm::mat4 projection = glm::perspective(glm::radians(45.0f), 1280.0f / 980.0f, 0.1f, 100.0f); // 定义投影矩阵
        //glm::vec4 vertex_positions[] = { // 定义顶点位置数组，这里定义了四个顶点的位置
        //    glm::vec4(-0.5f, -0.5f, 0.0f, 1.0f),
        //    glm::vec4(0.5f, -0.5f, 0.0f, 1.0f),
        //    glm::vec4(0.5f, 0.5f, 0.0f, 1.0f),
        //    glm::vec4(100.0f, 75.0f, 0.0f, 1.0f)
        //};
        //glm::vec4 result = glm_projection * vertex_positions[3]; // 计算投影矩阵和顶点位置的乘积，调试可以看到其具体的坐标计算后会在 [-1, 1] 区间
        glm::mat4 view = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, 0.0f)); // 定义模型视图投影矩阵，这里传入投影矩阵和模型矩阵，不做任何操作
        //glm::mat4 model = glm::translate(glm::mat4(1.0f), glm::vec3(-0.15f, 0.1f, 0.0f)); // 定义模型矩阵，这里传入模型矩阵，向（X 轴负方向）左移动 -0.15f 位置，向（Y 轴正方向）上移动 0.1f 位置，Z 轴不动，理解为百分比
        //glm::mat4 model_view_projection = model * view * glm_projection; // 定义模型视图投影矩阵，这里传入投影矩阵和模型矩阵

        //glm::vec4 vertex_colors[] = { // 定义顶点颜色数组，这里定义了四个顶点的颜色
        //    glm::vec4(1.0f, 0.0f, 0.0f, 1.0f),
        //    glm::vec4(0.0f, 1.0f, 0.0f, 1.0f),
        //    glm::vec4(0.0f, 0.0f, 1.0f, 1.0f),
        //};
        //M矩阵把模型投影到世界坐标，V矩阵相当于设置世界中的相机位置，P矩阵则是为了把相机所能拍摄到的东西投影到屏幕上（modelview-projection matrix）
        Shader shader(/*C:/Users/Administrator/Desktop/C++学习录/OpenGL/OpenGL基础/OpenGL基础/*/"Resource/shaders/MyMainShaders.shader"); // 定义着色器对象，这里传入着色器文件路径
        shader.get_source_code().print(); // 打印着色器代码
        /*ShaderSource shader_source_code = parse_shader("Resource/shaders/MyShaders.shader"); // 读取文件着色器代码，注意 vs 里默认文件相对路径是对于本项目工程文件的目录，这里为 Source 和 Resource 同级目录，这个默认的路径可以修改：右键项目 -> 属性 -> 调试 -> 工作目录 可根据需要修改
        cout << "Vertex shader code: " << shader_source_code.vertex_shader_code << endl; // 打印顶点着色器代码
        cout << "Fragment shader code: " << shader_source_code.fragment_shader_code << endl; // 打印片元着色器代码 */

        //std::string vertex_shader = shader_source_code.vertex_shader_code; // 获取顶点着色器代码
            /*"#version 330 core\n" // 定义版本号，这里使用 OpenGL 3.3
            "\n"
            "layout (location = 0) in vec4 position;\n"   // 定义输入的位置属性， location = 0 是索引，对应 glVertexAttribPointer 的第一个参数，vec4 对应第二个参数（这里让 OpenGL 自动转换数据类型）
            "layout (location = 0) in vec4 position;\n"
            "{\n"
            "    gl_Position = position;\n"     // 输出位置属性
            "}\n"; */
            //std::string fragment_shader = shader_source_code.fragment_shader_code; // 获取片元着色器代码
                /*R"(#version 330 core
                layout (location = 0) out vec4 color;
                void main()
                {
                    color = vec4(0.9f, 0.6f, 0.9f, 1.0f);
                }
            )"; */ // 输出颜色属性，这里参数依次是 RGBA 即红 0.9f、绿 0.5f、蓝 0.9f、透明度为 1.0f；注意注释不能写在原生字符串里否则显卡会报语法错误

        shader.use_program(); // 使用着色器程序（绑定着色器对象）
        /*uint32_t shader_program = create_shader(shader_source_code.vertex_shader_code, shader_source_code.fragment_shader_code); // 创建着色器程序
        if (!shader_program) {
            cout << "Shader program creation failed!" << endl;
            return -1;
        }
        gl_call(glUseProgram(shader_program)); // 使用着色器程序 */

        int location = shader.get_uniform_location("u_Color"); // 在 shader 文件中的 main 函数内没有使用则会检测不到
        /*gl_call(int location = glGetUniformLocation(shader_program, "our_color")); // 获取着色器程序中纹理的位置（location 是一个统一变量，在着色器程序中可以用它来获取 uniform 变量的位置）
        gl_assert(location != -1);  // 检查着色器程序中是否存在 uniform 变量 "our_color" */
        Texture texture(/*C:/Users/Administrator/Desktop/C++学习录/OpenGL/OpenGL基础/OpenGL基础/*/"Resource/textures/heart.png"); // 定义纹理对象，这里传入纹理文件路径这里是绝对路径，可修改为（Resource/textures/heart.png）这样项目工程文件移动也可以使用，上面的 shader 路径也是同理
        texture.bind();
        /*gl_call(glBindTexture(GL_TEXTURE_2D, 0)); // 绑定纹理，这里设置为 0 即默认纹理
        glUniform1i(glGetUniformLocation(shader_program, "u_color"), 0); // 设置纹理单元
        shader.set_uniform4f("our_color", 0.8f, 0.4f, 0.8f, 1.0f);// 设置初始颜色 */

        shader.set_uniform4f(location, 0.8f, 0.4f, 0.0f, 1.0f);     // 设置初始颜色
        //gl_call(glUniform4f(location, 0.8f, 0.4f, 0.8f, 1.0f));   // 设置初始颜色
        //shader.set_uniform_matrix4fv("m_MVP", view /*model_view_projection*/); // 设置模型视图投影矩阵
        //gl_call(glUniformMatrix4fv(glGetUniformLocation(shader_program, "m_model_view_projection"), 1, GL_FALSE, glm::value_ptr(glm_projection))); // 设置模型视图投影矩阵
        shader.set_uniform1i("u_Texture", 0); // 设置纹理单元
        //gl_call(glUniform1i(glGetUniformLocation(shader_program, "u_Texture"), 0)); // 设置纹理单元

        vertex_array.unbind(); // 解除绑定顶点数组对象
        //gl_call(glBindVertexArray(0)); // 解除绑定顶点数组

        shader.not_use_program();  // 解除使用着色器程序(解除绑定着色器程序)
        //gl_call(glUseProgram(0)); // 解除绑定着色器程序

        vertex_buffer.unbind(); // 解除绑定 VBO 缓冲
        //gl_call(glBindBuffer(GL_ARRAY_BUFFER, 0)); // 解除绑定 VBO 缓冲

        index_buffer.unbind();
        //gl_call(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); // 解除绑定 EBO 缓冲

        texture.unbind();

        Renderer renderer; // 定义渲染器对象

        // imgui 库初始化
        MyImGui my_imgui(0.932f, 0.510f, 0.932f, 1.00f); // 定义 ImGui 对象，这里传入初始化背景色
        /*const char* glsl_version = "#version 440 core"; // 定义 GLSL 版本号
        // Setup Dear ImGui context
        IMGUI_CHECKVERSION();
        ImGui::CreateContext();
        ImGuiIO& io = ImGui::GetIO(); (void)io;
        io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;     // Enable Keyboard Controls
        io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;      // Enable Gamepad Controls
        io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;         // Enable Docking
        io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;       // Enable Multi-Viewport / Platform Windows
        //io.ConfigViewportsNoAutoMerge = true;
        //io.ConfigViewportsNoTaskBarIcon = true;
        // Setup Dear ImGui style
        ImGui::StyleColorsDark();
        //ImGui::StyleColorsLight();
        // When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones.
        ImGuiStyle& style = ImGui::GetStyle();
        if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) {
            style.WindowRounding = 0.0f;
            style.Colors[ImGuiCol_WindowBg].w = 1.0f;
        } */
        // Setup Platform/Renderer backends
        my_imgui.set_up(window, "#version 440 core");
        /*ImGui_ImplGlfw_InitForOpenGL(window, true);
        ImGui_ImplOpenGL3_Init(glsl_version);*/

        // Our state
        bool show_demo_window = false;   // 初始化为隐藏
        bool show_another_window = false; // 初始化为隐藏
        //ImVec4 clear_color = ImVec4(0.932f, 0.510f, 0.932f, 1.00f); // 标准 pink 色

        float var_color = 0.0f;        // 定义颜色变量
        //float color_red = 1.0f;       // 定义颜色变量
        float color_increment = 0.02f;  // 定义颜色变化增量
        glm::mat4 init_mat4 = glm::mat4(1.0f); // 定义模型矩阵
        glm::vec3 translation_A(-0.1f, -0.1f, 0.0f);
        glm::vec3 translation_B(1.1f, -0.1f, 0.0f);


        // 测试框架的内容部分，在 Another Window 窗口中
        my_test::TestMenu* my_test_menu = new my_test::TestMenu(); // 自定义测试菜单对象
        my_test::TestClearColor my_clear_color(0.432f, 0.0f, 0.851f, 0.00f); // 自定义颜色对象
        my_test::TestTexture2D test_texture2d(2); // 自定义纹理对象
        //test_texture2d.bind();
        test_texture2d.unbind();
        my_test_menu->register_test("Test Clear Color", my_clear_color);
        my_test_menu->register_test("Test Texture", test_texture2d);
        //float test_rotation = 0.0f;

        /* Loop until the user closes the window 关闭窗口之前一直循环 */
        while (!glfwWindowShouldClose(window))
        {
            /* Renderer here 渲染 */
            //glClear(GL_COLOR_BUFFER_BIT);  // 循环清除屏幕内容
            renderer.clear(); // GLFW 清除屏幕内容

            /* Draw some stuff 绘制一些东西 */
            /* {
                glBegin(GL_TRIANGLES); // 开始绘制三角形

                glVertex2f(-0.5f, -0.5f);   // 第一个顶点
                glVertex2f(0.5f, -0.5f);    // 第二个顶点
                glVertex2f(0.0f, 0.5f);     // 第三个顶点

                //glColor3f(1.0f, 0.0f, 0.0f); // 设置三角形的颜色为红色
                //glVertex3f(-0.5f, -0.5f, 0.0f); // 第一个顶点
                //glColor3f(0.0f, 1.0f, 0.0f); // 设置三角形的颜色为绿色
                //glVertex3f(0.5f, -0.5f, 0.0f); // 第二个顶点
                //glColor3f(0.0f, 0.0f, 1.0f); // 设置三角形的颜色为蓝色
                //glVertex3f(0.0f, 0.5f, 0.0f); // 第三个顶点

                glEnd(); // 结束绘制三角形
            } */

            if (var_color < 0.0f)        // 循环改变的颜色
                color_increment = 0.01f;  // 颜色改变增加值
            else if (var_color > 1.0f)
                color_increment = -0.01f;
            //printf("var_color: %.2f\n", var_color); // 打印颜色值
            var_color += color_increment; // 改变颜色

            // Start the Dear ImGui frame
            my_imgui.new_frame();
            /*ImGui_ImplOpenGL3_NewFrame();
            ImGui_ImplGlfw_NewFrame();
            ImGui::NewFrame(); */

            if (show_demo_window)
                ImGui::ShowDemoWindow(&show_demo_window); // ImGui Demo Window 

            static float alpha_A = 1.0f;
            static float alpha_B = 1.0f;
            static int counter = 0;
            {
                ImGui::Begin("ImGui Main Window");                          // Create a window called "Hello, world!" and append into it.
                ImGui::Text("This is some useful text.");   //Display some text (you can use a format strings too)
                ImGui::Separator(); // 分割线

                ImGui::Text("Application average %.3f ms/frame (%.3f FPS)", 1000.0f / my_imgui.get_io().Framerate, my_imgui.get_io().Framerate);

                ImGui::Checkbox("Demo Window", &show_demo_window);      //Edit bools storing our window open/close state
                ImGui::Checkbox("Another Window", &show_another_window);

                ImGui::ColorEdit4("clear color (background color)", (float*)&my_imgui.get_clear_color()); //Edit 3 floats representing a color

                //ImGui::SliderFloat("translation_A X", &translation_A.x, -1.0f, 1.0f);            //Edit 1 float using a slider from -1.0f to 1.0f
                ////ImGui::ColorEdit3("clear color (background color)", (float*)&translation_A); //Edit 3 floats representing a color
                //ImGui::SliderFloat("translation_A Y", &translation_A.y, -1.0f, 1.0f, "-0.5f");            //Edit 1 float using a slider from -1.0f to 1.0f default value is -0.5f
                ImGui::SliderFloat3("translation_A", /*(float*)*/&translation_A[0], -0.5f, 1.5f);            //Edit 1 float using a slider from -1.0f to 1.0f default value is -0.5f，不愧是 C 语言，够疯狂！
                ImGui::SliderFloat("float alpha A", &alpha_A, 0.00f, 1.00f);            //Edit 1 float using a slider from 0.0f to 1.0f for alpha
                ImGui::SliderFloat3("translation_B", /*(float*)*/&translation_B[0], -0.5f, 1.5f);            //Edit 1 float using a slider from -1.0f to 1.0f default value is -0.5f
                ImGui::SliderFloat("float alpha B", &alpha_B, 0.00f, 1.00f);            //Edit 1 float using a slider from 0.0f to 1.0f for alpha

                if (ImGui::Button("Recover Button", ImVec2(100, 30))) {                            //Buttons return true when clicked (most widgets return true whenedited/activated)
                    counter++;
                    translation_A.x = -0.1f;
                    translation_B.x = 1.1f;
                    translation_A.y = translation_B.y = -0.1f;
                    translation_A.z = translation_B.z = 0.0f;
                    alpha_A = alpha_B = 1.0f;
                    cout << "Button clicked recovered，counter = " << counter << endl;
                }

                ImGui::SameLine();
                ImGui::Text("counter = %d", counter);

                ImGui::End();
            }


            // 3. Show another simple window. for test
            if (show_another_window) {
                dock_space(); // 靠边停放窗口功能
                ImGui::Begin("Another Window", &show_another_window);   // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked)
                ImGui::Text("Hello from another window!");

                my_test_menu->render();
                test_texture2d.shader->set_uniform4f("our_color", 0.8f, var_color, 0.5f, test_texture2d.m_alpha);
                /*if (my_test_menu->get_tests()[0].first) {
                    auto& in1 = my_test_menu->get_tests()[0].second;
                    in1.second.render();
                    in1.second.imgui_render();
                    if (ImGui::Button(in1.first.c_str(), ImVec2(150, 30)))
                        my_test_menu->get_tests()[0].first = true;
                }*/

                // 测试自己写的背景颜色
                //my_clear_color.imgui_render(); // 自定义颜色编辑对象
                //my_clear_color.render(); // 渲染自定义颜色对象，这样前面的颜色设置失效，测试这个需要把 MyImGui 类的 render 的 glClearColor 注释掉更明显

                if (ImGui::Button("Close Me", ImVec2(120, 50)))
                    show_another_window = false;
                ImGui::End();
            }

            // Rendering
            my_imgui.render(window); // 渲染 ImGui 内容
            /*ImGui::Render();
            int display_w, display_h;
            glfwGetFramebufferSize(window, &display_w, &display_h);
            glViewport(0, 0, display_w, display_h);
            glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w);
            glClear(GL_COLOR_BUFFER_BIT);
            ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());

            if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) {
                GLFWwindow* backup_current_context = glfwGetCurrentContext();
                ImGui::UpdatePlatformWindows();
                ImGui::RenderPlatformWindowsDefault();
                glfwMakeContextCurrent(backup_current_context);
            } */


            //vertex_array.bind(); // 绑定顶点数组对象
            //gl_call(glBindVertexArray(vertex_array_id)); // 绑定顶点数组对象

            //index_buffer.bind(); // 绑定索引缓冲区
            //gl_call(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer)); // 绑定 EBO 缓冲

            {
                glm::mat4 model = glm::translate(init_mat4, translation_A); // 定义模型矩阵，这里入模型矩阵，不做任何数学计算操作

                shader.use_program();

                shader.set_uniform4f(location, var_color, 0.33f, 0.88, alpha_A);
                //shader.set_uniform4f("our_color", 0.8f, 0.45f, var_color, 1.0f); // it's ok
                //gl_call(glUniform4f(location, 0.8f, 0.45f, var_color, 1.0f));

                shader.set_uniform_matrix4fv("m_MVP", model * view * glm_projection); // 设置模型视图投影矩阵
                texture.bind();
                renderer.draw(vertex_array, index_buffer, shader); // alt + f12 快速查看定义
                //gl_clean_error(); // 清除之前的 OpenGL 错误
                //glDrawArrays(GL_TRIANGLES, 0, 6); // 绘制三角形（一个或多个）
                //gl_call(glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr)); // 绘制索引数（一个或多个），注意这里的第三个参数必须是无符号的整型，如果是整型，则程序错误，不会绘制图形
                //gl_assert(gl_check_error()); // 检查 OpenGL 错误
            } // draw A heart

            {
                glm::mat4 model = glm::translate(init_mat4, translation_B);
                shader.use_program();
                shader.set_uniform4f(location, 0.95f, 0.33f, var_color, alpha_B);
                shader.set_uniform_matrix4fv("m_MVP", model * view * glm_projection);
                texture.bind();
                renderer.draw(vertex_array, index_buffer, shader); // alt + f12 快速查看定义
            } // draw B heart

            //ImGui::Render();
            //ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());

            /* Swap front and back buffers 交换前后缓冲 */
            glfwSwapBuffers(window);
            //glfwSwapBuffers(window2);

            /* Poll for and process events 轮询事件并处理事件 */
            glfwPollEvents();
        }
        delete my_test_menu;
        //gl_call(glDeleteProgram(shader_program));    // 删除着色器程序对象，释放资源
        //gl_call(glDeleteBuffers(1, &index_buffer));  // 删除 VBO 缓冲，释放资源

        /*ImGui_ImplOpenGL3_Shutdown();
        ImGui_ImplGlfw_Shutdown();
        ImGui::DestroyContext(); */
    }   // 添加作用域（position 到这）结束外部类中的循环
    glfwDestroyWindow(window);
    glfwTerminate();
}

int main()
{
    //GLFWwindow* window = nullptr;
    //GLFWwindow* window2 = nullptr; // 第二个窗口

    /*std::future<void> f1 = std::async(std::launch::async, Application, std::ref(window)); // std::ref 用于传递引用值
    std::future<void> f2 = std::async(std::launch::async, Application, std::ref(window2)); // 第二个窗口 err
    f1.wait(); // 等待第一个窗口退出
    f2.wait(); // 等待第二个窗口退出 */

    //std::thread t1(Application, std::ref(window)); // 第一个窗口
    //std::thread t2(Application, std::ref(window2)); // 第二个窗口 err
    //t1.join(); // 等待第一个窗口退出
    //t2.join(); // 等待第二个窗口退出

    Application(); // 第一个窗口

    //if (window)
    //    glfwDestroyWindow(window); /* Close the window 关闭窗口 */
    //if (window2)
    //    glfwDestroyWindow(window2); /* Close the window 关闭窗口 */
    //glfwTerminate(); /* Terminate GLFW 终止 GLFW */

    return 0;
}